﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using ESRI.ArcGIS.ArcMapUI;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Catalog;
using ESRI.ArcGIS.CatalogUI;
using ESRI.ArcGIS.DataManagementTools;
using ESRI.ArcGIS.DataSourcesGDB;
using ESRI.ArcGIS.Geodatabase;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Geoprocessor;
using ESRI.ArcGIS.esriSystem;

namespace BesAsm.Emgaats.Maintenance
{
    public partial class LinkToolSettingsForm : Form
    {
        HashSet<string> _reservedWords = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        public LinkToolSettingsForm()
        {
            InitializeComponent();

            _reservedWords.Add("delete");
        }

        protected override void OnLoad(EventArgs e)
        {
            // ImageList is used to set transparency
            appendPictureBox.Image = iconImageList.Images[0];
            archivePictureBox.Image = iconImageList.Images[1];

            _SetupDocumentEvents();

            _UpdateComboBoxes();
            _LoadSavedSettings();

            base.OnLoad(e);
        }

        private void okButton_Click(object sender, EventArgs e)
        {
            _SafeExecute(_OkClick);
        }

        private void _OkClick()
        {
            LinkToolSettings settings = EmgaatsMaintenanceExtension.Extension.Settings;
            IFeatureLayer appendLayer = appendComboBox.SelectedValue as IFeatureLayer;
            settings.AppendClass = appendLayer == null ? null : appendLayer.FeatureClass;

            IFeatureLayer archiveLayer = archiveComboBox.SelectedValue as IFeatureLayer;
            settings.ArchiveClass = archiveLayer == null ? null : archiveLayer.FeatureClass;

            this.Close();
        }

        private void _SafeExecute(Action method, bool showWaitCursor = true)
        {
            if (showWaitCursor)
                System.Windows.Forms.Cursor.Current = Cursors.WaitCursor;

            try
            {
                method();
            }
            catch (Exception ex)
            {
                _ShowError(ex);
            }
            finally
            {
                if (showWaitCursor)
                    System.Windows.Forms.Cursor.Current = Cursors.Default;
            }
        }

        private void _LoadLayersButton_Click(object sender, EventArgs e)
        {
            _SafeExecute(_AddMasterLinkLayers);
        }

        private void _AddMasterLinkLayers()
        {
            Configuration config = EmgaatsMaintenanceExtension.Extension.Configuration;

            IFeatureLayer linkLayer = new FeatureLayer();
            linkLayer.FeatureClass = config.MasterLinksEditClass;
            linkLayer.Name = linkLayer.FeatureClass.AliasName;
            ((IFeatureLayerDefinition)linkLayer).DefinitionExpression = "status = 'ACT'";

            IFeatureLayer nodeLayer = new FeatureLayer();
            nodeLayer.FeatureClass = config.MasterNodesEditClass;
            nodeLayer.Name = nodeLayer.FeatureClass.AliasName;
            ((IFeatureLayerDefinition)nodeLayer).DefinitionExpression = "status = 'ACT'";

            EnumLayer layers = new EnumLayer();
            layers.Add(linkLayer);
            layers.Add(nodeLayer);
            ArcMap.Document.FocusMap.AddLayers(layers, true);
        }

        private void _UpdateComboBoxes()
        {
            List<ComboBoxItem> polylineLayers = new List<ComboBoxItem>();

            IMxDocument document = ArcMap.Application.Document as IMxDocument;
            if (document != null)
            {
                polylineLayers.Add(new ComboBoxItem() { Name = "(no selection)", Value = null });

                IMap focusMap = document.FocusMap;
                try
                {
                    int layerCount = focusMap.LayerCount;
                    for (int i = 0; i < layerCount; i++)
                    {
                        ILayer layer = focusMap.get_Layer(i);
                        _CheckLayer(layer, polylineLayers);
                    }
                }
                finally
                {
                    EmgaatsMaintenanceExtension.ReleaseComObject(focusMap);
                }
            }

            _UpdateCombobox(appendComboBox, polylineLayers);
            _UpdateCombobox(archiveComboBox, polylineLayers);
        }

        private void _CheckLayer(ILayer layer, List<ComboBoxItem> polylineLayers)
        {
            bool layerUsed = false;
            if (layer is IGroupLayer && layer is ICompositeLayer)
            {
                ICompositeLayer groupLayer = (ICompositeLayer)layer;
                int layerCount = groupLayer.Count;
                for (int i = 0; i < layerCount; i++)
                {
                    ILayer subLayer = groupLayer.get_Layer(i);
                    _CheckLayer(subLayer, polylineLayers);
                }
            }
            else if (layer is IFeatureLayer)
            {
                if (((IFeatureLayer)layer).FeatureClass != null)
                {
                    if (((IFeatureLayer)layer).FeatureClass.ShapeType == esriGeometryType.esriGeometryPolyline)
                    {
                        polylineLayers.Add(new ComboBoxItem() { Name = layer.Name, Value = layer });
                        layerUsed = true;
                    }
                }
            }

            if (!layerUsed)
            {
                EmgaatsMaintenanceExtension.ReleaseComObject(layer);
            }
        }

        private void _UpdateCombobox(ComboBox comboBox, List<ComboBoxItem> dataSource)
        {
            if (dataSource != null)
            {
                // We make a copy so that each ComboBox has it's own datasource
                List<ComboBoxItem> dataSourceCopy = new List<ComboBoxItem>(dataSource);
                object selectedValue = comboBox.SelectedValue;
                comboBox.DataSource = dataSourceCopy;
                comboBox.DisplayMember = "Name";
                comboBox.ValueMember = "Value";
                if (selectedValue != null)
                    comboBox.SelectedValue = selectedValue;
            }
            else
            {
                comboBox.DataSource = null;
                comboBox.Items.Clear();
            }
        }

        private void _CreateAppendButton_Click(object sender, EventArgs e)
        {
            _SafeExecute(_CreateAppendLayer);
        }

        private void _CreateAppendLayer()
        {
            string location, name;
            if (_AskUserForOutputFeatureClass("New Append Layer", out location, out name))
            {
                IFeatureClass templateClass = EmgaatsMaintenanceExtension.Extension.Configuration.MasterLinksEditClass;
                string templatePath = EmgaatsMaintenanceExtension.Extension.Configuration.MasterSdeFile + "\\" + ((IDataset)templateClass).Name;
                string path = _CreateFeatureClassFromTemplate(templateClass, templatePath, location, name);
                IFeatureLayer appendLayer = EmgaatsMaintenanceExtension.Extension.FindFeatureLayer(path);
                if (appendLayer != null)
                {
                    EmgaatsMaintenanceExtension.AddField(appendLayer.FeatureClass, "ErrorText", esriFieldType.esriFieldTypeString, 100);
                    EmgaatsMaintenanceExtension.AddField(appendLayer.FeatureClass, "ErrorCode", esriFieldType.esriFieldTypeInteger);

                    EmgaatsMaintenanceExtension.Extension.Settings.AppendClass = appendLayer.FeatureClass;

                    string layerFilePath = EmgaatsMaintenanceExtension.GetDataFilePath("Append_Links_Default.lyr");
                    ILayerFile layerFile = new LayerFileClass();
                    if (layerFile.get_IsPresent(layerFilePath) && layerFile.get_IsLayerFile(layerFilePath))
                    {
                        layerFile.Open(layerFilePath);
                        ((IGeoFeatureLayer)appendLayer).Renderer = ((IGeoFeatureLayer)layerFile.Layer).Renderer;
                        ArcMap.Document.CurrentContentsView.Refresh(null);
                    }

                    ILayerFields layerFields = (ILayerFields)appendLayer;
                    FieldMapping fieldMapping = EmgaatsMaintenanceExtension.Extension.Configuration.LinkFieldMapping;
                    for (int f = 0; f < layerFields.FieldCount; f++)
                    {
                        string fieldName = layerFields.get_Field(f).Name;
                        if(fieldMapping.Contains(fieldName))
                            layerFields.get_FieldInfo(f).Visible = !fieldMapping[fieldName].Hidden;
                    }
                }
                _LoadSavedSettings();
            }
        }

        private void _CreateArchiveButton_Click(object sender, EventArgs e)
        {
            _SafeExecute(_CreateArchiveLayer);
        }

        private void _CreateArchiveLayer()
        {
            string location, name;
            if (_AskUserForOutputFeatureClass("New Delete Layer", out location, out name))
            {
                IFeatureClass templateClass = EmgaatsMaintenanceExtension.Extension.Configuration.MasterLinksEditClass;
                string templatePath = EmgaatsMaintenanceExtension.Extension.Configuration.MasterSdeFile + "\\" + ((IDataset)templateClass).Name;
                string path = _CreateFeatureClassFromTemplate(templateClass, templatePath, location, name);
                IFeatureLayer archiveLayer = EmgaatsMaintenanceExtension.Extension.FindFeatureLayer(path);
                if (archiveLayer != null)
                {
                    EmgaatsMaintenanceExtension.AddField(archiveLayer.FeatureClass, "ErrorText", esriFieldType.esriFieldTypeString, 100);
                    EmgaatsMaintenanceExtension.AddField(archiveLayer.FeatureClass, "ErrorCode", esriFieldType.esriFieldTypeInteger);

                    EmgaatsMaintenanceExtension.Extension.Settings.ArchiveClass = archiveLayer.FeatureClass;

                    string layerFilePath = EmgaatsMaintenanceExtension.GetDataFilePath("Delete_Links_Default.lyr");
                    ILayerFile layerFile = new LayerFileClass();
                    if (layerFile.get_IsPresent(layerFilePath) && layerFile.get_IsLayerFile(layerFilePath))
                    {
                        layerFile.Open(layerFilePath);
                        ((IGeoFeatureLayer)archiveLayer).Renderer = ((IGeoFeatureLayer)layerFile.Layer).Renderer;
                        ArcMap.Document.CurrentContentsView.Refresh(null);
                    }

                    ILayerFields layerFields = (ILayerFields)archiveLayer;
                    FieldMapping fieldMapping = EmgaatsMaintenanceExtension.Extension.Configuration.LinkFieldMapping;
                    for (int f = 0; f < layerFields.FieldCount; f++)
                    {
                        string fieldName = layerFields.get_Field(f).Name;
                        if(fieldMapping.Contains(fieldName))
                            layerFields.get_FieldInfo(f).Visible = !fieldMapping[fieldName].Hidden;
                    }
                }
                _LoadSavedSettings();
            }
        }

        private void cancelButton_Click(object sender, EventArgs e)
        {
            _SafeExecute(this.Close);
        }

        private bool _AskUserForOutputFeatureClass(string title, out string location, out string name)
        {
            IGxDialog gxDialog = new GxDialogClass();
            gxDialog.ObjectFilter = new GxFilterFeatureClassesClass();
            gxDialog.Title = "Save new feature class.";
            if (gxDialog.DoModalSave(ArcMap.Application.hWnd))
            {
                location = gxDialog.FinalLocation.FullName;
                name = gxDialog.Name;

                if (_reservedWords.Contains(name))
                {
                    string msg = string.Format("'{0}' is a reserved word and cannot be used. Please try again.", name);
                    MessageBox.Show(this, msg, "Invalid Name", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return false;
                }

                return true;
            }
            else
            {
                location = string.Empty;
                name = string.Empty;
                return false;
            }

        }

        private string _CreateFeatureClassFromTemplate(IFeatureClass template, string templatePath, string location, string name)
        {
            Geoprocessor gp = new Geoprocessor();
            
            CreateFeatureclass createTool = new CreateFeatureclass();
            createTool.geometry_type = _GetShapeType(template);
            createTool.template = templatePath;
            createTool.spatial_reference = ((IGeoDataset)template).SpatialReference;
            createTool.out_path = location;
            createTool.out_name = name;

            EmgaatsMaintenanceExtension.RunTool(gp, createTool, null);
            return createTool.out_feature_class as string;
        }

        private string _GetShapeType(IFeatureClass featureClass)
        {
            switch (featureClass.ShapeType)
            {
                case esriGeometryType.esriGeometryPolyline:
                    return "POLYLINE";
                case esriGeometryType.esriGeometryPoint:
                    return "POINT";
                case esriGeometryType.esriGeometryPolygon:
                    return "POLYGON";
                case esriGeometryType.esriGeometryMultipoint:
                    return "MULTIPOINT";
                default:
                    throw new NotSupportedException("The feature class shapetype is not supported");
            }
        }

        private string _GetFeatureClassPath(IFeatureClass featureClass)
        {
            string path = ((IDataset)featureClass).Workspace.PathName;
            if (featureClass.FeatureDataset != null)
            {
                path += "\\" + featureClass.FeatureDataset.Name;
            }
            path += "\\" + ((IDataset)featureClass).Name;
            return path;
        }

        private void _LoadSavedSettings()
        {
            LinkToolSettings settings = EmgaatsMaintenanceExtension.Extension.Settings;

            IFeatureLayer appendLayer = EmgaatsMaintenanceExtension.Extension.FindFeatureLayer(settings.AppendClass);
            if (appendLayer != null)
                appendComboBox.SelectedValue = appendLayer;

            IFeatureLayer archiveLayer = EmgaatsMaintenanceExtension.Extension.FindFeatureLayer(settings.ArchiveClass);
            if (archiveLayer != null)
                archiveComboBox.SelectedValue = archiveLayer;
        }

        private void _ShowError(Exception ex)
        {
            string msg = ex.GetType().FullName + ": " + ex.Message;
            msg += Environment.NewLine + ex.StackTrace;

            MessageBox.Show(this, msg, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        protected override void OnClosed(EventArgs e)
        {
            _RemoveDocumentEvents();
            base.OnClosed(e);
        }

        private void _SetupDocumentEvents()
        {
            //Listen to events which would change combobox contents
            IDocumentEvents_Event pNewDocEvents = (IDocumentEvents_Event)ArcMap.Document;
            pNewDocEvents.ActiveViewChanged += new IDocumentEvents_ActiveViewChangedEventHandler(this._OnFocusMapChanged);

            IActiveViewEvents_Event activeViewEvents = (IActiveViewEvents_Event)ArcMap.Document.ActiveView;
            activeViewEvents.FocusMapChanged += new IActiveViewEvents_FocusMapChangedEventHandler(this._OnFocusMapChanged);
            activeViewEvents.ItemAdded += new IActiveViewEvents_ItemAddedEventHandler(this._OnLayersChanged);
            activeViewEvents.ItemDeleted += new IActiveViewEvents_ItemDeletedEventHandler(this._OnLayersChanged);
            activeViewEvents.ItemReordered += new IActiveViewEvents_ItemReorderedEventHandler(this._OnLayersReordered);
        }

        private void _RemoveDocumentEvents()
        {
            IActiveViewEvents_Event activeViewEvents = (IActiveViewEvents_Event)ArcMap.Document.ActiveView;
            activeViewEvents.FocusMapChanged -= new IActiveViewEvents_FocusMapChangedEventHandler(this._OnFocusMapChanged);
            activeViewEvents.ItemAdded -= new IActiveViewEvents_ItemAddedEventHandler(this._OnLayersChanged);
            activeViewEvents.ItemDeleted -= new IActiveViewEvents_ItemDeletedEventHandler(this._OnLayersChanged);
            activeViewEvents.ItemReordered -= new IActiveViewEvents_ItemReorderedEventHandler(this._OnLayersReordered);

            IDocumentEvents_Event pDocEvents = (IDocumentEvents_Event)ArcMap.Document;
            pDocEvents.ActiveViewChanged -= new IDocumentEvents_ActiveViewChangedEventHandler(this._OnFocusMapChanged);
        }

        private void _OnFocusMapChanged()
        {
            _UpdateComboBoxes();
        }

        private void _OnLayersChanged(object layer)
        {
            _UpdateComboBoxes();
        }

        private void _OnLayersReordered(object layer, int position)
        {
            _UpdateComboBoxes();
        }

        private void _TestDbConnection()
        {
            //IFeatureLayer layer = ArcMap.Document.FocusMap.get_Layer(0) as IFeatureLayer;
            //IWorkspace workspace = ((IDataset)layer.FeatureClass).Workspace;
            //object names = null;
            //object values = null;
            //workspace.ConnectionProperties.GetAllProperties(out names, out values);
            //object[] namesArray = (object[])names;
            //object[] valuesArray = (object[])values;

            //string msg = string.Empty;
            //string nl = Environment.NewLine;
            //for (int i = 0; i < namesArray.Length; i++)
            //{
            //    msg += namesArray[i].ToString() + ": " + valuesArray[i].ToString() + nl;
            //}
            //textBox1.Text = msg;

            //IPropertySet propertySet = new PropertySetClass();
            //propertySet.SetProperty("SERVER", "GISDB1.ROSE.PORTLAND.LOCAL");
            //propertySet.SetProperty("INSTANCE", "SDE:SQLSERVER:GISDB1.ROSE.PORTLAND.LOCAL");
            //propertySet.SetProperty("DATABASE", "EGH_PUBLIC");
            //propertySet.SetProperty("AUTHENTICATION_MODE", "OSA");
            //propertySet.SetProperty("VERSION", "sde.DEFAULT");

            ////IPropertySet propertySet = new PropertySetClass();
            ////propertySet.SetProperty("SERVER", "BESDBDEV1");
            ////propertySet.SetProperty("INSTANCE", "SDE:SQLSERVER:BESDBDEV1");
            ////propertySet.SetProperty("DATABASE", "ASM_MASTER_RPT_SDE");
            ////propertySet.SetProperty("AUTHENTICATION_MODE", "OSA");
            ////propertySet.SetProperty("VERSION", "sde.DEFAULT");

            //IWorkspaceFactory workspaceFactory = new SdeWorkspaceFactoryClass();
            //IWorkspace ws = workspaceFactory.Open(propertySet, ArcMap.Application.hWnd);

            //IEnumDatasetName datasetNames = ws.get_DatasetNames(esriDatasetType.esriDTFeatureClass);

            //string datasets = string.Empty;
            //IDatasetName datasetName = datasetNames.Next();
            //while (datasetName != null)
            //{
            //    datasets += datasetName.Name + nl;
            //    datasetName = datasetNames.Next();
            //}
            //textBox1.Text = datasets;
        }
    }
}
